package com.suduku.entity;

import com.suduku.listener.SudoListener;
import com.suduku.listener.impl.SudoPrintImpl;
import com.suduku.util.SudoUtil;
import lombok.Getter;
import lombok.Setter;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 数独九宫 <br/>
 *
 * @author chena
 */
public class Sudo implements Cloneable, Serializable {

    /** 单元格列表 */
    @Getter
    private final List<Box> boxList;
    /** 行集合 */
    @Getter
    private Map<Integer, List<Box>> xMap;
    /** 列集合 */
    @Getter
    private Map<Integer, List<Box>> yMap;
    /** 宫集合 */
    @Getter
    private Map<Integer, List<Box>> gMap;
    /** 监听器 */
    @Getter
    @Setter
    private final SudoListener listener;
    /** 默认监听 */
    private static final SudoListener defaultListener = new SudoPrintImpl();

    public Sudo(List<Box> boxList) {
        this(boxList, defaultListener);
    }

    public Sudo(List<Box> boxList, SudoListener listener) {
        this.boxList = boxList;
        if(listener == null) {
            this.listener = defaultListener;
        } else {
            this.listener = listener;
        }
        initMap();
        initCList();
    }

    /**
     * 功能描述: 首次初始化候选数字列表，按照行，列，宫，清除已经出现的数字 <br/>
     *
     */
    private void initCList() {
        for(Box b : this.boxList) {
            if(b.isBlank()) {
                List<Integer> cList = new ArrayList<>(Box.INIT_LIST);
                List<Integer> xList = SudoUtil.getNotZero(this.xMap.get(b.getX()));
                List<Integer> yList = SudoUtil.getNotZero(this.yMap.get(b.getY()));
                List<Integer> gList = SudoUtil.getNotZero(this.gMap.get(b.getG()));
                cList.removeAll(xList);
                cList.removeAll(yList);
                cList.removeAll(gList);
                b.setCList(cList);
            }
        }
    }

    /**
     * 功能描述: 初始化x，y，g三个区域 <br/>
     *
     */
    private void initMap() {
        this.xMap = this.boxList.stream().collect(Collectors.groupingBy(Box::getX));
        this.yMap = this.boxList.stream().collect(Collectors.groupingBy(Box::getY));
        this.gMap = this.boxList.stream().collect(Collectors.groupingBy(Box::getG));
    }

    /**
     * 功能描述: 刷新其余单元格 <br/>
     *
     * @param box 单元格
     */
    public void refreshOtherBox(Box box) {
        if(box != null && box.isNumber()) {
            for(Box b : this.boxList) {
                if(!b.isInitNum() && b.getI() != box.getI() && (b.getX() == box.getX() || b.getY() == box.getY() || b.getG() == box.getG())) {
                    b.getCList().remove(box.getV());
                }
            }
        }
    }

    /**
     * 功能描述: 删除并重置的候选值 <br/>
     *
     * @param box 单元格
     */
    public void removeAndResetCList(Box box) {
        // 重置当前的候选值
        box.setV(0);
        initCList();
    }

    /**
     * 功能描述: 是否完成数独 <br/>
     *
     * @return "boolean"
     */
    public boolean isFinish() {
        return this.boxList.stream().noneMatch(Box::isBlank);
    }

    /**
     * 功能描述: 深度克隆 <br/>
     *
     * @return "com.suduku.entity.Sudo"
     */
    @Override
    public Sudo clone() {
        try {
            //将对象写入流
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(this);

            //将对象从流中取出
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
            ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
            return (Sudo) objectInputStream.readObject();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

}
